﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Camalot.Common.Extensions {
	public static partial class CamalotCommonExtensions {
		public static bool IsDayOfWeek ( this DateTime date, DayOfWeek dow ) {
			return date.DayOfWeek == dow;
		}

		public static bool IsEndOfAccountingPeriod ( this DateTime date ) {
			var d = date.Day;
			var dow = date.DayOfWeek;

			if ( dow == DayOfWeek.Friday ) {
				// last friday of the month?
				return date.AddDays ( 2 ).Month != date.Month;
			} else {
				return false;
			}
		}

		public static bool IsWeekDay ( this DateTime date ) {
			return !date.Date.IsDayOfWeek ( DayOfWeek.Saturday ) && !date.Date.IsDayOfWeek ( DayOfWeek.Sunday );
		}

		public static bool IsToday ( this DateTime date ) {
			var d1 = new DateTimeZone ( date );
			var d2 = new DateTimeZone ( DateTime.UtcNow );

			return DateTime.Compare ( d1.Local.Date, d2.Local.Date ) == 0;
		}

		public static bool IsSameDate ( this DateTime date, DateTime utcDate ) {
			var d1 = new DateTimeZone ( date );
			var d2 = new DateTimeZone ( utcDate );

			return DateTime.Compare ( d1.Local.Date, d2.Local.Date ) == 0;
		}

		public static DateTime ToKind ( this DateTime dt, DateTimeKind kind ) {
			return DateTime.SpecifyKind ( dt, kind );
		}

		public static String ToTimeString ( this DateTime date ) {
			return date.ToString ( "hh:mm:ss tt" );
		}

		public static DateTime FromUnixEpoch ( this long ticks ) {
			var date = new DateTime ( 1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc );
			return date.AddSeconds ( ticks );
		}

		public static long ToUnixEpoch ( this DateTime date ) {
			var epoc = new DateTime ( 1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc );

			if ( date.Kind == DateTimeKind.Local ) {
				date = new DateTimeZone ( date ).Utc;
			}

			var delta = date - epoc;
			if ( delta.TotalSeconds < 0 ) {
				throw new ArgumentOutOfRangeException (
						String.Format ( "Unix epoch starts January 1st, 1970 - {0}", date.ToString ( ) ) );
			}
			return (long)delta.TotalSeconds;
		}

		public static String ToFriendlyTime ( this DateTime? date ) {
			if ( date == null || !date.HasValue ) {
				return "never";
			} else {
				return ToFriendlyTime ( date.Value );
			}
		}

		public static String ToFriendlyTime ( this DateTime date ) {
			const int SECOND = 1;
			const int MINUTE = 60 * SECOND;
			const int HOUR = 60 * MINUTE;
			const int DAY = 24 * HOUR;
			const int WEEK = 7 * DAY;
			//const int MONTH = 30 * DAY;
			const int MONTH = (int)( 4.44 * WEEK );
			var ts = new TimeSpan ( DateTime.UtcNow.Ticks - date.Ticks );
			double delta = Math.Abs ( ts.TotalSeconds );

			if ( delta < 0 ) {
				return "not yet";
			}
			if ( delta == 0 ) {
				return "just now";
			}
			if ( delta < 1 * MINUTE ) {
				if ( ts.Seconds == 0 ) {
					return "just now";
				} else {
					return ts.Seconds == 1 ? "one second ago" : "{0} seconds ago".With ( ts.Seconds );
				}
			}
			if ( delta < 2 * MINUTE ) {
				return "a minute ago";
			}
			if ( delta < 45 * MINUTE ) {
				return "{0} minutes ago".With ( ts.Minutes );
			}
			if ( delta < 90 * MINUTE ) {
				return "an hour ago";
			}
			if ( delta < 24 * HOUR ) {
				return "{0} hour{1} ago".With ( ts.Hours, ts.Hours == 1 ? "" : "s" );
			}
			if ( delta < 48 * HOUR ) {
				return "yesterday";
			}
			if ( delta < 30 * DAY ) {
				return "{0} day{1} ago".With ( ts.Days, ts.Days == 1 ? "" : "s" );
			}

			if ( delta < 4.44 * WEEK ) {
				var weeks = ( ts.Days / 7 );
				return "{0} week{1} ago".With ( weeks, weeks == 1 ? "" : "s" );
			}

			if ( delta < 12 * MONTH ) {
				int months = Convert.ToInt32 ( Math.Floor ( (double)ts.Days / 30 ) );
				return months <= 1 ? "one month ago" : months + " months ago";
			} else {
				int years = Convert.ToInt32 ( Math.Floor ( (double)ts.Days / 365 ) );
				return years <= 1 ? "one year ago" : years + " years ago";
			}
		}
	}
}
